#pragma once
#include<iostream>
#include<cstring>
#include"StringUtil.h"
using namespace std;

enum TYPE{INT, LONG, FLOAT, DOUBLE, STR, BOOL};

class Basic{
public:

    virtual const char* getByteData() const = 0;

    virtual void setData(const char* byteData) = 0;

    virtual TYPE getType() const = 0;

    virtual double getHashVal() const = 0;

    virtual ~Basic(){};

    static int dataMaxLen;

    static void setBasicDataMaxLen(const int& dml){
        dataMaxLen = dml;
    }
};



class Int : public Basic{
private:
    int32_t data;
public:
    static const int byteNum = sizeof (data);

    Int(const Basic* b){
        if(b->getType() != INT)
            throw string("Type Error");
        this->setData(b->getByteData());
    }

    Int(const char* byteData){setData(byteData);}

    Int(const int32_t& data){this->data = data;}

    int32_t getData() const {return data;}

    void setData(const int32_t& data){this->data = data;}

    virtual const char* getByteData() const {return (char*)(&data);}

    virtual void setData(const char* byteData){memcpy(&data, byteData, byteNum);}

    virtual TYPE getType() const {return INT;}

    virtual double getHashVal() const {return data;}

    bool operator<(const Int& i)const{
        return this->data < i.data;
    }

    bool operator==(const Int& i)const{
        return this->data == i.data;
    }
};



class Long : public Basic{
private:
    int64_t data;
public:
    static const int byteNum = sizeof (data);

    Long(const Basic* b){
        if(b->getType() != LONG)
            throw string("Type Error");
        this->setData(b->getByteData());
    }

    Long(const char* byteData){setData(byteData);}

    Long(const int64_t& data){this->data = data;}

    long getData() const {return data;}

    void setData(const int64_t& data){this->data = data;}

    virtual const char* getByteData() const {return (char*)(&data);}

    virtual void setData(const char* byteData){memcpy(&data, byteData, byteNum);}

    virtual TYPE getType() const {return LONG;}

    virtual double getHashVal() const {return data;}

    bool operator<(const Long& l)const{
        return this->data < l.data;
    }

    bool operator==(const Long& l)const{
        return this->data == l.data;
    }
};



class Float : public Basic{
private:
    float data;
public:
    static const int byteNum = sizeof (data);

    Float(const Basic* b){
        if(b->getType() != FLOAT)
            throw string("Type Error");
        this->setData(b->getByteData());
    }

    Float(const char* byteData){setData(byteData);}

    Float(const float& data){this->data = data;}

    float getData() const {return data;}

    void setData(const float& data){this->data = data;}

    virtual const char* getByteData() const {return (char*)(&data);}

    virtual void setData(const char* byteData){memcpy(&data, byteData, byteNum);}

    virtual TYPE getType() const {return FLOAT;}

    virtual double getHashVal() const {return data;}

    bool operator<(const Float& f)const{
        return this->data < f.data;
    }

    bool operator==(const Float& f)const{
        return this->data == f.data;
    }
};



class Double : public Basic{
private:
    double data;
public:
    static const int byteNum = sizeof (data);

    Double(const Basic* b){
        if(b->getType() != DOUBLE)
            throw string("Type Error");
        this->setData(b->getByteData());
    }

    Double(const char* byteData){setData(byteData);}

    Double(const double& data){this->data = data;}

    double getData() const {return data;}

    void setData(const double& data){this->data = data;}

    virtual const char* getByteData() const {return (char*)(&data);}

    virtual void setData(const char* byteData){memcpy(&data, byteData, byteNum);}

    virtual TYPE getType() const {return DOUBLE;}

    virtual double getHashVal() const {return data;}

    bool operator<(const Double& d)const{
        return this->data < d.data;
    }

    bool operator==(const Double& d)const{
        return this->data == d.data;
    }
};



class Str : public Basic{
private:
    string data;

    hash<string> hashUtil;

    void init(const string& strData){
        StringUtil::checkLen(strData, dataMaxLen);
        this->data = strData;
    }

public:

    Str(const Basic* b){
        if(b->getType() != STR)
            throw string("Type Error");
        this->setData(b->getByteData());
    }

    Str(const char* byteData){
        init(byteData);
    }

    Str(const string& strData){
        init(strData);
    }

    string getData() const {
        return data; //在内存操作时不扩展到指定长度
    }

    int getByteNum() const { return dataMaxLen; }

    virtual const char* getByteData() const {
        return StringUtil::toByteData(data);
    }

    virtual void setData(const char* byteData){
        init(byteData);
    }

    virtual TYPE getType() const {return STR;}

    virtual double getHashVal() const {return hashUtil(data);}

    bool operator<(const Str& s)const{
        return this->data < s.data;
    }

    bool operator==(const Str& s)const{
        return this->data == s.data;
    }
};



class Bool : public Basic{
private:
    bool data;
public:
    static const int byteNum = sizeof (data);

    Bool(const Basic* b){
        if(b->getType() != BOOL)
            throw string("Type Error");
        this->setData(b->getByteData());
    }

    Bool(const char* byteData){setData(byteData);}

    Bool(const bool& data){this->data = data;}

    bool getData() const {return data;}

    void setData(const bool& data){this->data = data;}

    virtual const char* getByteData() const {return (char*)(&data);}

    virtual void setData(const char* byteData){memcpy(&data, byteData, byteNum);}

    virtual TYPE getType() const {return BOOL;}

    virtual double getHashVal() const {return data;}

    bool operator<(const Bool& b)const{
        return this->data < b.data;
    }

    bool operator==(const Bool& b)const{
        return this->data == b.data;
    }
};




bool basicEqu(const Basic* b1, const Basic* b2){
    const TYPE& dataType = b1->getType();
    if(dataType != b2->getType()){ throw string("type error"); }
    if(dataType == INT){
        return (*(Int*)b1) == (*(Int*)b2);
    }
    else if(dataType == LONG){
        return (*(Long*)b1) == (*(Long*)b2);
    }
    else if(dataType == FLOAT){
        return (*(Float*)b1) == (*(Float*)b2);
    }
    else if(dataType == DOUBLE){
        return (*(Double*)b1) == (*(Double*)b2);
    }
    else if(dataType == STR){
        return (*(Str*)b1) == (*(Str*)b2);
    }
    else if(dataType == BOOL){
        return (*(Bool*)b1) == (*(Bool*)b2);
    }
    else{
        throw string("type error");
    }
}





bool basicCmp(const Basic* b1, const Basic* b2){
    const TYPE& dataType = b1->getType();
    if(dataType != b2->getType()){ throw string("type error"); }
    if(dataType == INT){
        return (*(Int*)b1) < (*(Int*)b2);
    }
    else if(dataType == LONG){
        return (*(Long*)b1) < (*(Long*)b2);
    }
    else if(dataType == FLOAT){
        return (*(Float*)b1) < (*(Float*)b2);
    }
    else if(dataType == DOUBLE){
        return (*(Double*)b1) < (*(Double*)b2);
    }
    else if(dataType == STR){
        return (*(Str*)b1) < (*(Str*)b2);
    }
    else if(dataType == BOOL){
        return (*(Bool*)b1) < (*(Bool*)b2);
    }
    else{
        throw string("type error");
    }
}





class BasicUtil{
public:

    static void checkType(const Basic* data, const TYPE& dataType){
        if(data->getType() != dataType){
            throw string("type error");
        }
    }

    static void checkType(const vector<Basic*>& dataVec, const TYPE& dataType){
        for(Basic* b : dataVec){
            checkType(b, dataType);
        }
    }

    template<class T>
    static TYPE getType(const T& data){
        return getType<T>();
    };

    template<class T>
    static TYPE getType(){
        if(is_same<T, Int*>::value || is_same<T, int32_t>::value){
            return INT;
        }
        else if(is_same<T, Long*>::value || is_same<T, int64_t>::value){
            return LONG;
        }
        else if(is_same<T, Float*>::value || is_same<T, float>::value){
            return FLOAT;
        }
        else if(is_same<T, Double*>::value || is_same<T, double>::value){
            return DOUBLE;
        }
        else if(is_same<T, Str*>::value || is_same<T, string>::value){
            return STR;
        }
        else if(is_same<T, Bool*>::value || is_same<T, bool>::value){
            return BOOL;
        }
        else{
            throw string("type error");
        }
    }

    static Basic* byteDataToBasicData(const char* sourceData, const TYPE& dataType){
        if(dataType == INT){
            return new Int(sourceData);
        }
        else if(dataType == LONG){
            return new Long(sourceData);
        }
        else if(dataType == FLOAT){
            return new Float(sourceData);
        }
        else if(dataType == DOUBLE){
            return new Double(sourceData);
        }
        else if(dataType == STR){
            return new Str(sourceData);
        }
        else if(dataType == BOOL){
            return new Bool(sourceData);
        }
        else{
            throw string("type error");
        }
    }

    static Basic* getCopyBasicData(const Basic* basicData){
        if(basicData == nullptr){ return nullptr; }
        return byteDataToBasicData(basicData->getByteData(), basicData->getType());
    }

    static int getDataSize(const TYPE& dataType){
        if(dataType == INT){
            return Int::byteNum;
        }
        else if(dataType == LONG){
            return Long::byteNum;
        }
        else if(dataType == FLOAT){
            return Float::byteNum;
        }
        else if(dataType == DOUBLE){
            return Double::byteNum;
        }
        else if(dataType == STR){
            return Basic::dataMaxLen;
        }
        else if(dataType == BOOL){
            return Bool::byteNum;
        }
        else{
            throw string("type error");
        }
    }

    static int getBasicDataSize(Basic* basic){
        return getDataSize(basic->getType());
    }


    template<class T>
    static Basic* getBasicData(const T& data){
        const TYPE& dataType = getType(data);
        if(dataType == INT){
            return new Int(data);
        }
        else if(dataType == LONG){
            return new Long(data);
        }
        else if(dataType == FLOAT){
            return new Float(data);
        }
        else if(dataType == DOUBLE){
            return new Double(data);
        }
        else if(dataType == BOOL){
            return new Bool(data);
        }
        else{
            throw string("type error");
        }
    }

    static Basic* getBasicData(const string& data){
        return new Str(StringUtil::toByteData(data));
    }

    static string toStr(Basic* b){
        TYPE dataType = b->getType();
        if(dataType == INT){
            return to_string(((Int*)b)->getData());
        }
        else if(dataType == LONG){
            return to_string(((Long*)b)->getData());
        }
        else if(dataType == FLOAT){
            return to_string(((Float*)b)->getData());
        }
        else if(dataType == DOUBLE){
            return to_string(((Double*)b)->getData());
        }
        else if(dataType == BOOL){
            return to_string(((Bool*)b)->getData());
        }
        else if(dataType == STR){
            return b->getByteData();
        }
        else{
            throw string("type error");
        }
    }


};



